﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data.EntityClient;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using EncryptionV2V3.Entity;

namespace EncryptionV2V3
{
    public partial class DecryptionForm : Form
    {
        public String[] Files;
        private const string Title = "Document Migration V2 to V3";

        readonly BackgroundWorker _backgroundWorker0 = new BackgroundWorker();
        readonly BackgroundWorker _backgroundWorker1 = new BackgroundWorker();

        private string _outputPath = "";
        private const string PathChunk = "\\tmp";

        private const int Count = 32;

        private const string Password = "myKey123";
        private const string StaticString = "!H@3dN^tt2C$a4k%";
        private static bool _proceed = false;

        private static List<bool> v2Erros;
        private static List<bool> v3Erros;

        public DecryptionForm()
        {
            InitializeComponent();

            // Create a background worker thread that ReportsProgress &
            // SupportsCancellation
            // Hook up the appropriate events.
            _backgroundWorker0.DoWork += new DoWorkEventHandler(backgroundWorker0_DoWork);
            _backgroundWorker0.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                    (backgroundWorker0_ProgressCompleted);
            _backgroundWorker0.WorkerReportsProgress = true;
            _backgroundWorker0.WorkerSupportsCancellation = true;

            _backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            _backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
                    (backgroundWorker1_ProgressChanged);
            _backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                    (backgroundWorker1_ProgressCompleted);
            _backgroundWorker1.WorkerReportsProgress = true;
            _backgroundWorker1.WorkerSupportsCancellation = true;
        }


        private void BrowseButton1_Click(object sender, EventArgs e)
        {
            try
            {
                Files = GetFiles();
                if (Files != null)
                {
                    PathTextBox1.Text = Files.Length + " PDF documents selected.";
                    if (Files.Length == 1)
                    {
                        PathTextBox1.Text = Files.Length + " PDF document selected.";
                    }
                }
                else
                {
                    PathTextBox1.Text = "";
                }
            }
            catch (Exception)
            {
                ExceptionHandler();
            }
        }

        private void BrowseButton2_Click(object sender, EventArgs e)
        {
            try
            {
                DialogResult result = folderBrowserDialog1.ShowDialog();
                if (result == DialogResult.OK)
                {
                    PathTextBox2.Text = folderBrowserDialog1.SelectedPath;
                }
            }
            catch (Exception)
            {
                ExceptionHandler();
            }
        }

        private void MigrateButton_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(IpPortTextBox.Text.Trim()) ||
                    string.IsNullOrEmpty(DatabaseNameTextBox.Text.Trim()) ||
                    string.IsNullOrEmpty(UsernameTextBox.Text.Trim()) ||
                    string.IsNullOrEmpty(PasswordTextBox.Text.Trim()))
                {
                    MessageBox.Show("Please set connectivity information to continue.", Title);
                }
                else if (Files == null || Files.Length == 0)
                {
                    MessageBox.Show("Please select one or more PDF files to continue.", Title);
                }
                else if (PathTextBox2.Text == "")
                {
                    MessageBox.Show("Please select a destination folder for V3 documents.", Title);
                }
                else
                {
                    infoLabel.Visible = true;
                    infoLabel.Text = "Connecting to the database...";

                    MigrateButton.Text = "Processing...";
                    MigrateButton.Enabled = false;

                    progressBar1.Style = ProgressBarStyle.Marquee;
                    _backgroundWorker0.RunWorkerAsync();
                }
            }
            catch (Exception)
            {
                MigrateButton.Text = "Migrate";
                MigrateButton.Enabled = true;
                infoLabel.Visible = false;
                ExceptionHandler();
            }
        }

        private void MigrateButtonProcess2()
        {
            if (_proceed)
            {
                progressBar1.Style = ProgressBarStyle.Continuous;
                _outputPath = PathTextBox2.Text;

                if (Files == null || Files.Length == 0)
                {
                    MessageBox.Show("Please select one or more PDF files to continue.", Title);
                }
                else if (PathTextBox2.Text == "")
                {
                    MessageBox.Show("Please select a destination folder for V3 documents.", Title);
                }
                else
                {
                    if (MessageBox.Show("Are you sure you want to migrate V2 documents to V3?", "Confirm Migration", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        MigrateButton.Text = "Processing...";
                        MigrateButton.Enabled = false;
                        _backgroundWorker1.RunWorkerAsync();
                    }
                    else
                    {
                        MigrateButton.Text = "Migrate";
                        MigrateButton.Enabled = true;
                        progressBar1.Value = 0;
                    }
                }
            }
        }


        private void backgroundWorker0_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                SetConnectionString();
                _proceed = ConnectivityTest();
            }
            catch (Exception)
            {
                _proceed = false;
            }
        }

        private void backgroundWorker0_ProgressCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                if (!_proceed)
                {
                    infoLabel.Visible = false;
                    progressBar1.Style = ProgressBarStyle.Continuous;
                    MigrateButton.Enabled = true;
                    MigrateButton.Text = "Migrate";
                    MessageBox.Show("Database connectivity failed. Please check connectivity information.", Title);
                }
                if (_proceed)
                {
                    infoLabel.Text = "Successfully connected to the database.";
                    MigrateButtonProcess2();
                }
            }
            catch (Exception)
            {
                progressBar1.Style = ProgressBarStyle.Continuous;
                MigrateButton.Enabled = true;
                ExceptionHandler();
            }
            infoLabel.Visible = false;
        }


        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {

                double i = 0;
                double len = Files.Length;
                v2Erros = new List<bool>();
                v3Erros = new List<bool>();

                foreach (string filename in Files)
                {
                    if (File.Exists(filename)) //check is file is there
                    {
                        {
                            string[] array = filename.Split('\\');
                            string outfilename = array[array.Length - 1];
                            string v2Outfilepath = _outputPath + PathChunk + "\\" + outfilename;

                            string path = _outputPath + PathChunk;
                            if (!Directory.Exists(path))
                            {
                                DirectoryInfo di = Directory.CreateDirectory(path);
                                di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
                            }

                            bool v2Stat = DecryptV2(filename, v2Outfilepath);
                            if (!v2Stat)
                                v2Erros.Add(false);

                            string v3OutfileName = _outputPath + "\\" + Path.GetFileName(filename); //.Replace("pdf", "bpaper");
                            bool v3Stat = EncryptToV3(v2Outfilepath, v3OutfileName, outfilename);
                            if (!v3Stat)
                                v3Erros.Add(false);

                            if (File.Exists(v2Outfilepath))
                            {
                                //File.Delete(v2Outfilepath);
                            }

                            double progress = (double)((++i / len) * 100);
                            int finalProgress = (int)Math.Ceiling(progress);
                            _backgroundWorker1.ReportProgress(finalProgress);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler2(ex);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = Title + " (" + e.ProgressPercentage.ToString() + "%)";
        }

        private void backgroundWorker1_ProgressCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            string dir = _outputPath + PathChunk;
            try
            {
                Directory.Delete(dir, true);
            }
            catch (Exception)
            {
            }

            progressBar1.Value = 0;
            this.Text = Title;
            MigrateButton.Text = "Migrate";
            MigrateButton.Enabled = true;
            infoLabel.Visible = false;

            PathTextBox1.Text = "";
            PathTextBox2.Text = "";

            string ErrMsg = "Migration Completed with Failures.";
            if (v2Erros != null && v2Erros.Count > 0)
            {
                if (v2Erros.Count == 1)
                {
                    ErrMsg += "\nV2 Decryption Failed on " + v2Erros.Count + " File.";
                }
                else
                {
                    ErrMsg += "\nV2 Decryption Failed on " + v2Erros.Count + " Files.";
                }
            }
            if (v3Erros != null && v3Erros.Count > 0)
            {
                if (v3Erros.Count == 1)
                {
                    ErrMsg += "\nV3 Encryption Failed on " + v3Erros.Count + " File.";
                }
                else
                {
                    ErrMsg += "\nV3 Encryption Failed on " + v3Erros.Count + " Files.";
                }
            }

            if (v2Erros != null && v2Erros.Count > 0 || v3Erros != null && v3Erros.Count > 0)
            {
                MessageBox.Show(ErrMsg, Title);
            }
            else
            {
                MessageBox.Show("Migration Completed Successfully.", Title);
            }
        }


        public string[] GetFiles()
        {
            try
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog
                                                         {
                                                             Multiselect = true,
                                                             CheckFileExists = false,
                                                             Filter = "Board Papers (*.bpaper)|*.bpaper",
                                                             FilterIndex = 2,
                                                             RestoreDirectory = false
                                                         };

                DialogResult result = openFileDialog1.ShowDialog(); // Show the dialog.
                if (result == DialogResult.OK)
                {
                    return openFileDialog1.FileNames;
                }
            }
            catch (Exception e)
            {
                ExceptionHandler2(e);
            }
            return null;
        }


        public bool DecryptV2(string inputFilePath, string outputFilePath)
        {
            FileStream fsOut = null;
            CryptoStream cs = null;
            FileStream fsCrypt = null;

            try
            {
                UnicodeEncoding ue = new UnicodeEncoding();
                byte[] key = ue.GetBytes(Password);

                fsCrypt = new FileStream(inputFilePath, FileMode.Open);  // Here should come try catch unless IOExeption can occur
                RijndaelManaged rmCrypto = new RijndaelManaged();
                cs = new CryptoStream(fsCrypt, rmCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read);
                fsOut = new FileStream(outputFilePath, FileMode.Create);

                int data;
                while ((data = cs.ReadByte()) != -1)
                    fsOut.WriteByte((byte)data);
                               
                return true;
            }
            catch (Exception e)
            {
                ExceptionHandler2(e);
            }
            finally
            {
                if (fsOut != null)
                    fsOut.Close();
                if (cs != null)
                    cs.Close();
                if (fsCrypt != null)
                    fsCrypt.Close();
            }
            return false;
        }

        public bool EncryptToV3(string inputFilePath, string outputFilePath, string fileName)
        {
            FileStream fsIn = null;
            CryptoStream csS = null;
            FileStream filetoEncrypt = null;

            try
            {
                string Encrypter = String.Empty;
                using (BoardAppProductEntities entities = new BoardAppProductEntities())
                {
                    Paper paperEntity = (from p in entities.Papers
                                         join d in entities.DocVersions on p.VersionId equals d.VersionId
                                         where d.FilePath.Contains(fileName)
                                         select p).FirstOrDefault();

                    Encrypter = (paperEntity.CreatedDate.ToString().Replace(" ", "_") + StaticString).Substring(0, Count - 1);

                }
                //Set up the encryption objects
                using (AesCryptoServiceProvider acsp = GetProvider(Encoding.Default.GetBytes(Encrypter)))
                {
                    ICryptoTransform ictE = acsp.CreateEncryptor();
                    filetoEncrypt = new FileStream(outputFilePath, FileMode.Create);

                    //Perform the encryption, storing output into the stream
                    using (csS = new CryptoStream(filetoEncrypt, ictE, CryptoStreamMode.Write))
                    {
                        using (fsIn = new FileStream(inputFilePath, FileMode.Open))
                        {

                            int data;
                            while ((data = fsIn.ReadByte()) != -1)
                                csS.WriteByte((byte)data);
                        }
                    }
                }
                return true;
            }
            catch (Exception e)
            {
                ExceptionHandler2(e);
            }
            finally
            {
                fsIn.Close();
                csS.Close();
                filetoEncrypt.Close();
            }
            return false;
        }

        private static AesCryptoServiceProvider GetProvider(byte[] key)
        {
            AesCryptoServiceProvider result = new AesCryptoServiceProvider();
            result.BlockSize = 128;
            result.KeySize = 256;
            result.Mode = CipherMode.CBC;
            result.Padding = PaddingMode.PKCS7;

            result.GenerateIV();
            result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            byte[] realKey = GetKey(key, result);
            result.Key = realKey;
            // result.IV = RealKey;
            return result;
        }

        private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
        {
            byte[] kRaw = suggestedKey;
            List<byte> kList = new List<byte>();

            for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8)
            {
                kList.Add(kRaw[(i / 8) % kRaw.Length]);
            }
            byte[] k = kList.ToArray();
            return k;
        }


        private bool ConnectivityTest()
        {
            try
            {
                using (BoardAppProductEntities entities = new BoardAppProductEntities())
                {
                    entities.Connection.Open();
                    return true;
                }
                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private void SetConnectionString()
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

                // Because it's an EF connection string it's not a normal connection string
                // so we pull it into the EntityConnectionStringBuilder instead
                EntityConnectionStringBuilder efb = new EntityConnectionStringBuilder(
                        config.ConnectionStrings.ConnectionStrings["BoardAppProductEntities"].ConnectionString);

                // Then we extract the actual underlying provider connection string
                SqlConnectionStringBuilder sqb = new SqlConnectionStringBuilder(efb.ProviderConnectionString);

                // Now we can set the data source
                sqb.DataSource = IpPortTextBox.Text.Trim();
                sqb.InitialCatalog = DatabaseNameTextBox.Text.Trim();
                sqb.UserID = UsernameTextBox.Text.Trim();
                sqb.Password = PasswordTextBox.Text.Trim();

                // Pop it back into the EntityConnectionStringBuilder 
                efb.ProviderConnectionString = sqb.ConnectionString;

                // And update...
                config.ConnectionStrings.ConnectionStrings["BoardAppProductEntities"]
                    .ConnectionString = efb.ConnectionString;

                config.Save(ConfigurationSaveMode.Modified, true);
                ConfigurationManager.RefreshSection("connectionStrings");
            }
            catch (Exception)
            {
                MessageBox.Show("Connectivity section encounter an error.", Title);
            }
        }


        private void ExceptionHandler()
        {
            MessageBox.Show("Application Error. Please try again.", Title);
        }

        private void ExceptionHandler2(Exception e)
        {
            MessageBox.Show("Exception:\n" + e.Message + "\n\nStackTrace:\n" + e.StackTrace + "\n\nInner Exception:\n" + e.InnerException, Title);
        }

    }
}
